/**
 * @file bsp_uart.c
 * @brief
 * @author  ()
 * @version 1.0
 * @date 2024-09-13
 *
 * @copyright Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 *
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-09-13 <td>1.0     <td>     <td>内容
 * </table>
 */
#include "bsp_uart.h"
#include "global.h"
#include "hal_adapter.h"
#include "cmsis_os2.h"
#include "public/ring_buffer.h"
#include <stdarg.h>

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include "bsp.h"

UART_HandleTypeDef comm_uart_ctx = {
    .Instance = COMM_UART_PERIHERAL,
    .Init.BaudRate = 115200,
    .Init.WordLength = UART_WORDLENGTH_8B,
    .Init.StopBits = UART_STOPBITS_1,
    .Init.Parity = UART_PARITY_NONE,
    .Init.Mode = UART_MODE_TX_RX,
    .Init.HwFlowCtl = UART_HWCONTROL_NONE,
    .Init.OverSampling = UART_OVERSAMPLING_16,
    .Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE,
    .Init.ClockPrescaler = UART_PRESCALER_DIV1,
    .AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT,
};

UART_HandleTypeDef debug_uart_ctx = {
    .Instance = DEBUG_UART_PERIHERAL,
    .Init.BaudRate = 115200,
    .Init.WordLength = UART_WORDLENGTH_8B,
    .Init.StopBits = UART_STOPBITS_1,
    .Init.Parity = UART_PARITY_NONE,
    .Init.Mode = UART_MODE_TX_RX,
    .Init.HwFlowCtl = UART_HWCONTROL_NONE,
    .Init.OverSampling = UART_OVERSAMPLING_16,
    .Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE,
    .Init.ClockPrescaler = UART_PRESCALER_DIV1,
    .AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT,
};

UART_HandleTypeDef modem_uart_ctx = {
    .Instance = MODEM_UART_PERIHERAL,
    .Init.BaudRate = 115200,
    .Init.WordLength = UART_WORDLENGTH_8B,
    .Init.StopBits = UART_STOPBITS_1,
    .Init.Parity = UART_PARITY_NONE,
    .Init.Mode = UART_MODE_TX_RX,
    .Init.HwFlowCtl = UART_HWCONTROL_NONE,
    .Init.OverSampling = UART_OVERSAMPLING_16,
    .Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE,
    .Init.ClockPrescaler = UART_PRESCALER_DIV1,
    .AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT,
};

UART_HandleTypeDef gnss_uart_ctx = {
    .Instance = GNSS_UART_PERIHERAL,
    .Init.BaudRate = 115200,
    .Init.WordLength = UART_WORDLENGTH_8B,
    .Init.StopBits = UART_STOPBITS_1,
    .Init.Parity = UART_PARITY_NONE,
    .Init.Mode = UART_MODE_TX_RX,
    .Init.HwFlowCtl = UART_HWCONTROL_NONE,
    .Init.OverSampling = UART_OVERSAMPLING_16,
    .Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE,
    .Init.ClockPrescaler = UART_PRESCALER_DIV1,
    .AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT,
};

static uart_periph_t uart_tab[] = {
#ifdef STM32U575xx
    {debug_uart_id, {3, 0}, DEBUG_IRQn, &debug_uart_ctx, NULL, NULL, NULL, NULL},
    {modem_uart_id, {2, 0}, MODEM_IRQn, &modem_uart_ctx, NULL, NULL, NULL, NULL},
    {gnss_uart_id,  {4, 0}, GNSS_IRQn, &gnss_uart_ctx, NULL, NULL, NULL, NULL},
    {comm_uart_id,  {5, 0}, COMM_IRQn, &comm_uart_ctx, NULL, NULL, NULL, NULL},
#endif

#ifdef STM32H563xx
    {debug_uart_id, {2, 0}, DEBUG_IRQn, &debug_uart_ctx, NULL, NULL, NULL, NULL},
    {modem_uart_id, {3, 0}, MODEM_IRQn, &modem_uart_ctx, NULL, NULL, NULL, NULL},
    {gnss_uart_id,  {4, 0}, GNSS_IRQn, &gnss_uart_ctx, NULL, NULL, NULL, NULL},
     {comm_uart_id,  {4, 0}, COMM_IRQn, &comm_uart_ctx, NULL, NULL, NULL, NULL},
#endif
};

extern void Error_Handler(void);

/**
 * @brief Bsp UART init.Need to implement the UART_HandleTypeDef struct above manually.
 *
 * @param num port number of USART
 * @return int32_t
 */
int32_t bsp_uart_init(int16_t num)
{
    uint16_t i = 0;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
        Error_Handler();

    DDL_ZERO_STRUCT(uart_periph->rx_ring_buff);
    DDL_ZERO_STRUCT(uart_periph->tx_ring_buff);

    if (HAL_UART_Init(uart) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_UARTEx_SetTxFifoThreshold(uart, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_UARTEx_SetRxFifoThreshold(uart, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_UARTEx_DisableFifoMode(uart) != HAL_OK)
    {
        Error_Handler();
    }

    /* USART interrupt Init here!*/
    HAL_NVIC_SetPriority(uart_periph->isr_num, uart_periph->isr_prio[0], uart_periph->isr_prio[1]);
    HAL_NVIC_EnableIRQ(uart_periph->isr_num);

    __HAL_USART_CLEAR_TXFECF(uart);
    __HAL_USART_ENABLE_IT(uart, USART_IT_RXNE);

    return 0;
}

/// @brief
/// @param buf
/// @param count
/// @return
ssize_t bsp_uart_itwrite(int16_t num, const void *buf, size_t count)
{
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;
    uint16_t i = 0;
    uint32_t j = 0;
    uint32_t wr_cnt = 0;
    uint8_t *temp = (uint8_t *)buf;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
        Error_Handler();
    
    taskENTER_CRITICAL();

    for (j = 0; j < count; j++)
    {
        if (1 == ringBuf_Write(&uart_periph->tx_ring_buff, *temp))
            wr_cnt++;
        temp++;
    }

    __HAL_USART_DISABLE_IT(uart, USART_IT_TC);
    __HAL_USART_CLEAR_TXFECF(uart);
    __HAL_USART_ENABLE_IT(uart, USART_IT_TXE);

    taskEXIT_CRITICAL();

    return wr_cnt;
}

/*
 * @brief       function for reading data from the UART and save it into the rx_rbuff.
 * @param[in]   none
 * @return      i --> length of the data which has been successfully read.
 */
ssize_t bsp_uart_read(int16_t num, void *buffer, size_t size)
{
    int32_t i = 0;
    int16_t j = 0;
    uint8_t *buf_ptr = buffer;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;

    SEARCH_LIST(j, uart_tab)
    {
        if (uart_tab[j].num == num)
        {
            uart = uart_tab[j].uart;
            uart_periph = &uart_tab[j];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
        Error_Handler();

    __HAL_USART_ENABLE_IT(uart, USART_IT_RXNE);

    taskENTER_CRITICAL();

    for (i = 0; i < size; i++)
    {
        if (ringBuf_Read(&uart_periph->rx_ring_buff, (uint8_t *)&buf_ptr[i]) == 0)
            break;
    }

    taskEXIT_CRITICAL();

    return i;
}


/**
 * @brief
 *
 * @param num
 */
inline void hdl_serial_interrupt_handler(int16_t num)
{
    unsigned char data = 0;
    int16_t i = 0;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
        Error_Handler();

    if (__HAL_USART_GET_IT(uart, USART_IT_TXE) == SET)
    {
        // buff不为空发送字节，为空时确保关闭TXE中断
        if (ringBuf_Read(&uart_periph->tx_ring_buff, &data) == SET)
        {
            uart->Instance->TDR = (uint8_t)(data & 0xFFU);
        }
        else
        {
            __HAL_USART_DISABLE_IT(uart, USART_IT_TXE);
        }
    }

    if (__HAL_USART_GET_FLAG(uart, USART_FLAG_RXNE) == SET)
    {
        ringBuf_Write(&uart_periph->rx_ring_buff, uart->Instance->RDR);
    }

    if (__HAL_USART_GET_IT(uart, USART_IT_ORE) == SET)
    {
        __HAL_USART_CLEAR_FLAG(uart, USART_ICR_ORECF);
    }
}

/**
 * @brief 串口设置波特率
 * @param  num              串口ID
 * @param  bps              波特率
 * @return int32_t
 */
int32_t bsp_uart_set_baudrate(int16_t num, uint32_t bps)
{
    int16_t i = 0;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
    {
        Error_Handler();
        return -EINVAL;
    }

    HAL_UART_DeInit(uart);

    uart->Init.BaudRate = bps;

    if (HAL_UART_Init(uart) != HAL_OK)
    {
        Error_Handler();
        return -EINVAL;
    }

    if (HAL_UARTEx_DisableFifoMode(uart) != HAL_OK)
    {
        Error_Handler();
        return -EINVAL;
    }

    /* USART interrupt Init here!*/
    HAL_NVIC_SetPriority(uart_periph->isr_num, uart_periph->isr_prio[0], uart_periph->isr_prio[1]);
    HAL_NVIC_EnableIRQ(uart_periph->isr_num);

    __HAL_USART_CLEAR_TXFECF(uart);
    __HAL_USART_ENABLE_IT(uart, USART_IT_RXNE);

    return 0;
}


int32_t tx_buf_clr(int16_t num)
{
    int16_t i = 0;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
    {
        Error_Handler();
        return -EINVAL;
    }

    ringBuf_clear(&uart_periph->tx_ring_buff);

    return SUCC;
}


int32_t rx_buf_clr(int16_t num) 
{
    int16_t i = 0;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
    {
        Error_Handler();
        return -EINVAL;
    }

    ringBuf_clear(&uart_periph->rx_ring_buff);  

    return SUCC;
}


int32_t tx_buf_flush(int16_t num)
{
    int16_t i = 0;
    UART_HandleTypeDef *uart = NULL;
    uart_periph_t *uart_periph = NULL;
    uint8_t data = 0;

    SEARCH_LIST(i, uart_tab)
    {
        if (uart_tab[i].num == num)
        {
            uart = uart_tab[i].uart;
            uart_periph = &uart_tab[i];
            break;
        }
    }

    if (uart_periph == NULL || uart == NULL)
    {
        Error_Handler();
        return -EINVAL;
    }

    unsigned int flush_exec_start = getms();
    vTaskSuspendAll();
    while(ringBuf_Read(&uart_periph->tx_ring_buff, &data)) {
        HAL_UART_Transmit(uart, &data, 1, 50);
        if(timeout_ms(flush_exec_start, 50))
            break;
    }
    xTaskResumeAll();
    return SUCC;
}


/**
 * @brief  Retargets the C library printf function to the USART.
 * @param  None
 * @retval None
 */
PUTCHAR_PROTOTYPE
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART1 and Loop until the end of transmission */
     bsp_uart_itwrite(debug_uart_id, (uint8_t *)&ch, 1);

    return ch;
}

/**
 * @brief 等待串口发送完成
 * @param  num              My Param doc
 */
void bsp_uart_waitfor_transmit_finished(int16_t num)
{

}


/**
 * @brief This function handles USART1 global interrupt.
 */
void USART1_IRQHandler(void)
{
    hdl_serial_interrupt_handler(1);
}

/**
 * @brief This function handles USART2 global interrupt.
 */
void USART2_IRQHandler(void)
{
    hdl_serial_interrupt_handler(2);
}

/**
 * @brief This function handles USART3 global interrupt.
 */
void USART3_IRQHandler(void)
{
    hdl_serial_interrupt_handler(3);
}

/**
 * @brief This function handles USART4 global interrupt.
 */
void UART4_IRQHandler(void)
{
    hdl_serial_interrupt_handler(4);
}

/**
 * @brief This function handles USART5 global interrupt.
 */
void UART5_IRQHandler(void)
{
    hdl_serial_interrupt_handler(5);
}

/**
 * @brief This function handles USART6 global interrupt.
 */
void USART6_IRQHandler(void)
{
    hdl_serial_interrupt_handler(6);
}

/* HARDFAULT 信息存储 */
#define HARDFAULT_LOG_LEN      2048
__no_init unsigned char hardfault_msg[HARDFAULT_LOG_LEN];
uint32_t hardfault_msg_idx = 0;

/**
 * @brief 用于段错误时的打印接口
 * @note  仅用于段错误打印, 其他正常打印不允许使用, 会导致数据丢失
 * @param  fmt              需要打印的数据
 * @return                  0 <= 实际发送的数据, 负数 - 错误码
 */
int32_t cmb_printf(const char *fmt, ...)
{
    va_list args;
    int32_t num     = 0;
    HAL_StatusTypeDef status = HAL_ERROR;

    va_start(args, fmt);
    num = vsnprintf((char*)&hardfault_msg[hardfault_msg_idx], HARDFAULT_LOG_LEN - hardfault_msg_idx, fmt, args);
    va_end(args);

    if (0 < num) {
        status = HAL_UART_Transmit(&debug_uart_ctx, &hardfault_msg[hardfault_msg_idx], num, 1000);
        hardfault_msg_idx += num;
    }

    if (HAL_OK == status)
        return num;

    return -EIO;
}
